package sna.model;
import java.util.Map;
import java.util.Random;
import edu.uci.ics.jung.graph.*;
import edu.uci.ics.jung.algorithms.metrics.Metrics;
import org.apache.commons.collections15.Transformer;
import edu.uci.ics.jung.algorithms.shortestpath.DistanceStatistics;

public class BAModel {
	
	private int currentVID;
	private int currentEID;
	private int totalEdgeNum;
	private int totalVertexNum;
	Time t = new Time();
	UndirectedGraph<MyVertex,MyEdge> graph;
	
	private class Time{
		private int time;
		public int getTime() {
			return time;
		}

		public void setTime(int time) {
			this.time = time;
		}

		void passTime(){
			for (MyVertex vTemp : graph.getVertices()){
				vTemp.setAge(vTemp.getAge()+1);
			}	
			//attachment
			attach();
			detach();
			//edge Generation
			//edge Degeneration
			time++;
		}
		
		void attach(){
			int num = Math.max(totalVertexNum/100,10);
			Random r = new Random();
			MyVertex[] newNode = new MyVertex[num];
			for (int i=0;i<num;i++){
				newNode[i] = new MyVertex(0,currentVID,0);
				currentVID++;
	            graph.addVertex(newNode[i]);
	            totalVertexNum++;
	            double d = r.nextDouble();
	            int numTemp = (int)((double)totalEdgeNum * d * 2);
	            //addEgde
	            for (MyVertex vtemp : graph.getVertices()){
	            	int edgeNum=vtemp.getEdgeNum();
	            	numTemp-=edgeNum;
	              	//System.out.println(numTemp);
	            	if (numTemp<=0){
	            		graph.addEdge(new MyEdge(currentEID),vtemp,newNode[i]);
	            		totalEdgeNum++;
	            		currentEID++;
	            		vtemp.setEdgeNum(vtemp.getEdgeNum()+1);
	            		newNode[i].setEdgeNum(1);
	            		for (MyVertex v2: graph.getNeighbors(vtemp)){
	            		// p=0.6;
	            			double p=0.1;
	            			if (r.nextDouble()<p){
	            				graph.addEdge(new MyEdge(currentEID),v2,newNode[i]);
	            				v2.setEdgeNum(v2.getEdgeNum()+1);
	            				newNode[i].setEdgeNum(newNode[i].getEdgeNum()+1);
	            				currentEID++;
	            				totalEdgeNum++;
	            			}
	            		}
	            		
	            		break;
	            	}
	            	

	            }
	            
			}
		}
		
		void detach(){
			//TODO
		}
	};
	
	
	BAModel(){
		
		graph = new UndirectedSparseGraph<MyVertex,MyEdge>();
		
		graph.addVertex(new MyVertex(0, 0, 0));
		graph.addVertex(new MyVertex(0, 1, 0));
		graph.addVertex(new MyVertex(0, 2, 0));
		graph.addVertex(new MyVertex(0, 3, 0));
		graph.addVertex(new MyVertex(0, 4, 0));
		
		for(MyVertex v1 : graph.getVertices()){
			for(MyVertex v2 : graph.getVertices()){
				if(v1.getID() != v2.getID()){
					graph.addEdge(new MyEdge(currentEID), v1, v2);
					currentEID++;
					v1.setEdgeNum(v1.getEdgeNum()+1);
					v2.setEdgeNum(v2.getEdgeNum()+1);
				}
			}
		}
		
		currentVID = 5;
		this.totalEdgeNum = 10;
		this.totalVertexNum = 5;
		/*for (int i=0;i<5;i++){
			graph.addEdge(new MyEdge(i),new MyVertex(1,2*i,0), new MyVertex(1,2*i+1,0));	
			totalEdgeNum++;
			totalVertexNum+=2;
		}
		currentVID=10;
		currentEID=5;*/
	}
	
	//Print edge number v.s. vertex number
	public void output(){
		//System.out.println(t.getTime());
		int[] a = new int[10000];
		for (MyVertex vTemp: graph.getVertices()){
			a[graph.getNeighborCount(vTemp)]++;
		}		
		for(int i=0;i<10000;i++){
			System.out.println(i+" "+a[i]);
		}
	} 
	
	public double calculateCC(){
		double CC = 0;
		Map<MyVertex, Double> map = Metrics.clusteringCoefficients(this.graph);
		for(MyVertex v : this.graph.getVertices()){
			//System.out.println(map.get(v));
			CC += map.get(v);
		}
		CC /= this.graph.getVertexCount();
		return CC;		
	}
	
	public static void main(String argv[]){
		
		BAModel model = new BAModel();
		while (model.totalVertexNum < 10000 ){			
			model.t.passTime();
			//System.out.println(model.totalVertexNum);
		}
		
		//Print edge number v.s. vertex number(Power law of out degree distribution)
		model.output();
		
		//Calculating the clustering coefficient
		double CC = model.calculateCC();
		System.out.println("CC = " + CC);
		
		//Calculating the average shortest path length
		Transformer<MyVertex, Double> t = DistanceStatistics.averageDistances(model.graph);
		double avgShortestPathLength = 0;
		for(MyVertex v : model.graph.getVertices()){
			double avgPL = t.transform(v);
			//System.out.println(avgPL);
			avgShortestPathLength += avgPL;
		}
		avgShortestPathLength /= model.graph.getVertexCount();
		System.out.println("average shortest path length = " + avgShortestPathLength);
		
		//Positive assortativity (assortativity coefficient)
		//TODO
		
		//Giant component size
		//TODO
		
		//Densification power law
		//number of nodes v.s. number of vertex
		//TODO
		
		//Shrinking diameter
		
		
		
		
	}
}
